using System;

namespace Tycho.Parser.Peg {
    public class InterpolatedStringProduction : Production {
        private readonly Func<char, char> translateEscapeCharacter;
        private readonly IProduction InterpolationProduction;

        public InterpolatedStringProduction (IProduction interpolationProduction) : this (interpolationProduction, TranslateEscapeCharacter) {
        }

        public InterpolatedStringProduction (IProduction interpolationProduction, Func<char, char> translateEscapeCharacter) {
            this.translateEscapeCharacter = translateEscapeCharacter;
            InterpolationProduction = interpolationProduction;

            ErrorInformation = new ProductionErrorInformation ("interpolated-string");
        }

        public static Terminal CreateTerminal (IProduction interpolationProduction) {
            return new Terminal (new InterpolatedStringProduction (interpolationProduction));
        }

        protected override ParseResult ReallyParse (char[] source, int startIndex, ParseContext context, string sourceString, ParseEnvironment parseEnvironment) {
            if (source [startIndex] == '"') {
                int index = startIndex;
                if (++index >= source.Length) return null;
                int startOfString = index;

                var interpolatedStringBuilder = new InterpolatedStringBuilder (startIndex,
                                                                               parseEnvironment.SourceFileInformation.CreateSourceInformation);

                while (source [index] != '"') {
                    char c = source[index];
                    if (c == '\\') {
                        interpolatedStringBuilder.AddString (ExtractString (source, startOfString, index), index);

                        if (++index >= source.Length) return null;

                        interpolatedStringBuilder.AddString (translateEscapeCharacter(source [index]).ToString(), index);

                        if (++index >= source.Length) return null;

                        startOfString = index;
                        continue;
                    } else if (c == '#') {
                        interpolatedStringBuilder.AddString (ExtractString (source, startOfString, index), index);

                        if (++index >= source.Length) return null;

                        var interpolatedResult = InterpolationProduction.Parse (source, index, context, sourceString, parseEnvironment);

                        if (interpolatedResult != null) {
                            interpolatedStringBuilder.AddTerm (interpolatedResult.Term, interpolatedResult.Index);

                            index = interpolatedResult.Index;
                            if (index >= source.Length) return null;
                            startOfString = index;
                            continue;
                        } else {
                            return null;
                        }
                    }

                    if (++index >= source.Length) return null;
                }
                interpolatedStringBuilder.AddString (ExtractString (source, startOfString, index), index);

                index++;

                ITerm resultTerm = interpolatedStringBuilder.CreateTerm (index);

                return new ParseResult (index, resultTerm, context);
            } else {
                return null;
            }
        }

        private static string ExtractString (char[] source, int startOfString, int index) {
            return new string (source, startOfString, index - startOfString);
        }

        public static char TranslateEscapeCharacter (char source) {
            switch (source) {
                case 't':
                    return '\t';
                case 'n':
                    return '\n';
                case 'r':
                    return '\r';
                default:
                    return source;
            }
        }
    }
}